<?php

if (!defined('BASEPATH'))
  exit('No direct script access allowed');
/**
 * CodeIgniter
 *
 * An open source application development framework for PHP 5.1.6 or newer
 *
 * @package		CodeIgniter
 * @author		ExpressionEngine Dev Team
 * @copyright	Copyright (c) 2008 - 2011, EllisLab, Inc.
 * @license		http://codeigniter.com/user_guide/license.html
 * @link		http://codeigniter.com
 * @since		Version 1.3.1
 * @filesource
 */
// ------------------------------------------------------------------------

/**
 * Unit Testing Class
 *
 * Simple testing class
 *
 * @package		CodeIgniter
 * @subpackage	Libraries
 * @category	UnitTesting
 * @author		ExpressionEngine Dev Team
 * @link		http://codeigniter.com/user_guide/libraries/uri.html
 */
class CI_Unit_test {

  var $active = TRUE;
  var $results = array();
  var $strict = FALSE;
  var $_template = NULL;
  var $_template_rows = NULL;
  var $_test_items_visible = array();

  public function __construct() {
    // These are the default items visible when a test is run.
    $this->_test_items_visible = array(
        'test_name',
        'test_datatype',
        'res_datatype',
        'result',
        'file',
        'line',
        'notes'
    );

    log_message('debug', "Unit Testing Class Initialized");
  }

  // --------------------------------------------------------------------

  /**
   * Run the tests
   *
   * Runs the supplied tests
   *
   * @access	public
   * @param	array
   * @return	void
   */
  function set_test_items($items = array()) {
    if (!empty($items) AND is_array($items)) {
      $this->_test_items_visible = $items;
    }
  }

  // --------------------------------------------------------------------

  /**
   * Run the tests
   *
   * Runs the supplied tests
   *
   * @access	public
   * @param	mixed
   * @param	mixed
   * @param	string
   * @return	string
   */
  function run($test, $expected = TRUE, $test_name = 'undefined', $notes = '') {
    if ($this->active == FALSE) {
      return FALSE;
    }

    if (in_array($expected, array('is_object', 'is_string', 'is_bool', 'is_true', 'is_false', 'is_int', 'is_numeric', 'is_float', 'is_double', 'is_array', 'is_null'), TRUE)) {
      $expected = str_replace('is_float', 'is_double', $expected);
      $result = ($expected($test)) ? TRUE : FALSE;
      $extype = str_replace(array('true', 'false'), 'bool', str_replace('is_', '', $expected));
    } else {
      if ($this->strict == TRUE)
        $result = ($test === $expected) ? TRUE : FALSE;
      else
        $result = ($test == $expected) ? TRUE : FALSE;

      $extype = gettype($expected);
    }

    $back = $this->_backtrace();

    $report[] = array(
        'test_name' => $test_name,
        'test_datatype' => gettype($test),
        'res_datatype' => $extype,
        'result' => ($result === TRUE) ? 'passed' : 'failed',
        'file' => $back['file'],
        'line' => $back['line'],
        'notes' => $notes
    );

    $this->results[] = $report;

    return($this->report($this->result($report)));
  }

  // --------------------------------------------------------------------

  /**
   * Generate a report
   *
   * Displays a table with the test data
   *
   * @access	public
   * @return	string
   */
  function report($result = array()) {
    if (count($result) == 0) {
      $result = $this->result();
    }

    $CI = & get_instance();
    $CI->load->language('unit_test');

    $this->_parse_template();

    $r = '';
    foreach ($result as $res) {
      $table = '';

      foreach ($res as $key => $val) {
        if ($key == $CI->lang->line('ut_result')) {
          if ($val == $CI->lang->line('ut_passed')) {
            $val = '<span style="color: #0C0;">' . $val . '</span>';
          } elseif ($val == $CI->lang->line('ut_failed')) {
            $val = '<span style="color: #C00;">' . $val . '</span>';
          }
        }

        $temp = $this->_template_rows;
        $temp = str_replace('{item}', $key, $temp);
        $temp = str_replace('{result}', $val, $temp);
        $table .= $temp;
      }

      $r .= str_replace('{rows}', $table, $this->_template);
    }

    return $r;
  }

  // --------------------------------------------------------------------

  /**
   * Use strict comparison
   *
   * Causes the evaluation to use === rather than ==
   *
   * @access	public
   * @param	bool
   * @return	null
   */
  function use_strict($state = TRUE) {
    $this->strict = ($state == FALSE) ? FALSE : TRUE;
  }

  // --------------------------------------------------------------------

  /**
   * Make Unit testing active
   *
   * Enables/disables unit testing
   *
   * @access	public
   * @param	bool
   * @return	null
   */
  function active($state = TRUE) {
    $this->active = ($state == FALSE) ? FALSE : TRUE;
  }

  // --------------------------------------------------------------------

  /**
   * Result Array
   *
   * Returns the raw result data
   *
   * @access	public
   * @return	array
   */
  function result($results = array()) {
    $CI = & get_instance();
    $CI->load->language('unit_test');

    if (count($results) == 0) {
      $results = $this->results;
    }

    $retval = array();
    foreach ($results as $result) {
      $temp = array();
      foreach ($result as $key => $val) {
        if (!in_array($key, $this->_test_items_visible)) {
          continue;
        }

        if (is_array($val)) {
          foreach ($val as $k => $v) {
            if (FALSE !== ($line = $CI->lang->line(strtolower('ut_' . $v)))) {
              $v = $line;
            }
            $temp[$CI->lang->line('ut_' . $k)] = $v;
          }
        } else {
          if (FALSE !== ($line = $CI->lang->line(strtolower('ut_' . $val)))) {
            $val = $line;
          }
          $temp[$CI->lang->line('ut_' . $key)] = $val;
        }
      }

      $retval[] = $temp;
    }

    return $retval;
  }

  // --------------------------------------------------------------------

  /**
   * Set the template
   *
   * This lets us set the template to be used to display results
   *
   * @access	public
   * @param	string
   * @return	void
   */
  function set_template($template) {
    $this->_template = $template;
  }

  // --------------------------------------------------------------------

  /**
   * Generate a backtrace
   *
   * This lets us show file names and line numbers
   *
   * @access	private
   * @return	array
   */
  function _backtrace() {
    if (function_exists('debug_backtrace')) {
      $back = debug_backtrace();

      $file = (!isset($back['1']['file'])) ? '' : $back['1']['file'];
      $line = (!isset($back['1']['line'])) ? '' : $back['1']['line'];

      return array('file' => $file, 'line' => $line);
    }
    return array('file' => 'Unknown', 'line' => 'Unknown');
  }

  // --------------------------------------------------------------------

  /**
   * Get Default Template
   *
   * @access	private
   * @return	string
   */
  function _default_template() {
    $this->_template = "\n" . '<table style="width:100%; font-size:small; margin:10px 0; border-collapse:collapse; border:1px solid #CCC;">';
    $this->_template .= '{rows}';
    $this->_template .= "\n" . '</table>';

    $this->_template_rows = "\n\t" . '<tr>';
    $this->_template_rows .= "\n\t\t" . '<th style="text-align: left; border-bottom:1px solid #CCC;">{item}</th>';
    $this->_template_rows .= "\n\t\t" . '<td style="border-bottom:1px solid #CCC;">{result}</td>';
    $this->_template_rows .= "\n\t" . '</tr>';
  }

  // --------------------------------------------------------------------

  /**
   * Parse Template
   *
   * Harvests the data within the template {pseudo-variables}
   *
   * @access	private
   * @return	void
   */
  function _parse_template() {
    if (!is_null($this->_template_rows)) {
      return;
    }

    if (is_null($this->_template)) {
      $this->_default_template();
      return;
    }

    if (!preg_match("/\{rows\}(.*?)\{\/rows\}/si", $this->_template, $match)) {
      $this->_default_template();
      return;
    }

    $this->_template_rows = $match['1'];
    $this->_template = str_replace($match['0'], '{rows}', $this->_template);
  }

}

// END Unit_test Class

/**
 * Helper functions to test boolean true/false
 *
 *
 * @access	private
 * @return	bool
 */
function is_true($test) {
  return (is_bool($test) AND $test === TRUE) ? TRUE : FALSE;
}

function is_false($test) {
  return (is_bool($test) AND $test === FALSE) ? TRUE : FALSE;
}

/* End of file Unit_test.php */
/* Location: ./system/libraries/Unit_test.php */